<template>
  <div class="gs-upload">
    <n-upload
      class="upload"
      ref="uploadRef"
      action="#"
      :accept="props.accept"
      :disabled="props.disabled"
      :directory-dnd="props.directoryDnd"
      :default-upload="true"
      :custom-request="request"
      :max="props.max"
      @before-upload="beforeUpload"
      @remove="removeFile"
      block
      :show-remove-button="true"
    >
      <!-- 拖拽模式 -->
      <n-upload-dragger v-if="props.directoryDnd">
        <div style="margin-bottom: 12px">
          <n-icon size="48" :depth="3">
            <CloudUploadOutlined />
          </n-icon>
        </div>
        <n-text style="font-size: 16px"> 点击或者拖动文件到该区域来上传 </n-text>
        <n-p depth="3" style="margin: 8px 0 0 0">
          {{ props.tip }}
        </n-p>
      </n-upload-dragger>
      <!-- 按钮模式 -->
      <n-space vertical :size="16" v-else>
        <n-button type="primary">
          <template #icon>
            <n-icon>
              <CloudUploadOutlined />
            </n-icon>
          </template>
          选择文件
        </n-button>
        <n-p>{{ props.tip }}</n-p>
      </n-space>
    </n-upload>
  </div>
</template>
<script setup lang="ts">
  import { getMinioinfo } from '@/components/GsUpload/api/uploadApi';
  import { useMessage } from 'naive-ui';
  import { dayjs } from 'element-plus';
  import { CloudUploadOutlined } from '@vicons/antd';
  import SparkMD5 from 'spark-md5';
  import axios from 'axios';
  const emit = defineEmits(['beforeUpload', 'uploadSuccess', 'remove', 'getMd5']);
  const message = useMessage();
  const http = axios.create();
  const props = defineProps({
    // 是否只读
    readonly: Boolean,
    // 是否支持目录拖拽
    directoryDnd: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    tip: {
      type: String,
      default: '提示配置',
    },
    max: {
      type: Number,
      default: 5,
    },
    // 存储桶名称
    bucketName: {
      type: String,
      default: '',
    },
    // 上传路径前缀
    fullPath: {
      type: String,
      default: dayjs().valueOf() + '-',
    },
    // 上传格式
    accept: {
      type: String,
      default: '',
    },
  });
  // 特殊字符验证
  function isValidFilename(filename) {
    // 定义正则表达式，包括字母、数字、下划线和连字符
    var validPattern = /^[a-zA-Z0-9_.-]+$/;
    // 使用正则表达式测试文件名
    return validPattern.test(filename);
  }
  // 上传前验证
  const beforeUpload = async (file) => {
    // 限制桶名不为空
    if (!props.bucketName) {
      message.warning(`请在组件上设置桶名`);
      return false;
    }
    // 限制文件名称不能带特殊字符
    if (!isValidFilename(file?.file?.name)) {
      message.warning(`文件名不能包含特殊字符或中文`);
      return false;
    }
    // 限制文件大小
    if (file.size / (1024 * 1024) > 100) {
      message.warning(`当前限制文件大小不能大于100M`);
      return false;
    }
    // 限制上传格式
    // console.log(file);
    // if (props.accept.indexOf(file.type) === -1) {
    //   message.warning(`当前限制上传格式为${props.accept}`);
    //   return false;
    // }
    // 文件生成md5
    const md5Val = await getFileAllMD5(file.file.file);
    emit('getMd5', md5Val);

    // 发送上传前的钩子
    emit('beforeUpload');
  };

  // 移除文件
  const removeFile = () => {
    emit('remove');
  };
  // 上传方法
  async function request({ file, onFinish, onError, onProgress }) {
    const name = props.fullPath + file.name; // 文件名
    file.name = name;
    const params = {
      bucket_name: props.bucketName,
      object_name: name,
      url_type: 1,
    };
    try {
      const { code, data, msg } = await getMinioinfo(params);
      if (code === 200) {
        // 开始上传
        http
          .put(data.url, file.file, {
            onUploadProgress: (progressEvent) => {
              onProgress({
                percent: Math.ceil((progressEvent.loaded * 100) / progressEvent.total),
              });
            },
          })
          .then(({ status }) => {
            if (status === 200) {
              file.url = data.url.split('?')[0];
              emit('uploadSuccess', data.url);
              onFinish();
            } else {
              message.error(msg);
              onError();
            }
          })
          .catch(() => {
            onError();
            message.error('上传失败2');
          });
      } else {
        message.error(msg);
      }
    } catch (error) {
      onError();
    }
  }

  /**
   * 获取文件的MD5数值
   * @param {*} file
   */
  function getFileAllMD5(file: File) {
    return new Promise((resolve, reject) => {
      const fileReader = new FileReader();
      fileReader.readAsBinaryString(file);
      fileReader.onload = (e: any) => {
        resolve(SparkMD5.hashBinary(e.target.result, false));
      };
      fileReader.onerror = function () {
        message.error('文件读取出错，请检查该文件！');
        reject(new Error('文件读取出错，请检查该文件！'));
      };
    });
  }
</script>

<style lang="less" scoped>
  .gs-upload {
    width: 100%;
    margin-bottom: 0;
    padding-bottom: 0;
    :deep(.n-upload-trigger) {
      width: 100%;
    }
  }
</style>
